package mornd.test4;

import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: mornd
 * @dateTime: 2023/5/6 - 20:11
 * 统一转换为环绕通知
 */
public class Demo {
    /*
       高级切面转为低级切面
     */
    public static void main(String[] args) throws Throwable {

        // 切面实例对象，一份就行
        AspectInstanceFactory aspectInstanceFactory =
                new SingletonAspectInstanceFactory(new Target1());

        Method[] declaredMethods = MyAspect.class.getDeclaredMethods();

        // 保存所有的低级切面集合
        List<Advisor> advisors = new ArrayList<>();

        for (Method m : declaredMethods) {
            if (m.isAnnotationPresent(Before.class)) {
                // 配置切点表达式
                String expression = m.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                // 专门处理 before 的通知
/*
                new AspectJAroundAdvice(); // 环绕 （已以实现MethodInterceptor接口，本省就算环绕通知）
                new AspectJAfterAdvice();  // 最终 （已实现MethodInterceptor接口，本省就算环绕通知 ）
                new AspectJAfterReturningAdvice(); // 后置
                new AspectJAfterThrowingAdvice();  // 异常 （已实现MethodInterceptor接口，本省就算环绕通知 ）
                */
                AspectJMethodBeforeAdvice advice =
                        new AspectJMethodBeforeAdvice(m, pointcut, aspectInstanceFactory);

                // 配置切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                advisors.add(advisor);
            } else if(m.isAnnotationPresent(After.class)) {
                // after
                String expression = m.getAnnotation(After.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                AspectJAfterAdvice afterAdvice =
                        new AspectJAfterAdvice(m, pointcut, aspectInstanceFactory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, afterAdvice);
                advisors.add(advisor);
            } else if(m.isAnnotationPresent(Around.class)) {
                // around
                String expression = m.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                AspectJAroundAdvice aroundAdvice =
                        new AspectJAroundAdvice(m, pointcut, aspectInstanceFactory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, aroundAdvice);
                advisors.add(advisor);
            }
        }

        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }

        // 统一转换为环绕通知 （适配器设计模式：一套接口转换为另一套接口，以便适合某种场景的调用）
        Target1 target1 = new Target1();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target1);
        // 最外层添加 spring 自带的通知，用于将用户定义的通知放到当前线程的 ThreadLocal 中获取
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
        proxyFactory.addAdvisors(advisors);
        List<Object> aroundAdviceList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(
                Target1.class.getDeclaredMethod("foo"), Target1.class);

        System.out.println("------------------");
        for (Object o : aroundAdviceList) {
            System.out.println(o);
        }

        // 创建并执行调用链 x
//        Constructor<ReflectiveMethodInvocation> constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
//        constructor.setAccessible(true);
//        ReflectiveMethodInvocation methodInvocation = constructor.newInstance(null, target1, Target1.class.getDeclaredMethod("foo"), new Object[0],
//                Target1.class, aroundAdviceList);
//
//        Object proceed = methodInvocation.proceed();

        MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
                null, target1, Target1.class.getDeclaredMethod("foo"), new Object[0],
                Target1.class, aroundAdviceList) {};

        Object proceed = methodInvocation.proceed();

    }

    @Aspect
    static class MyAspect {

        @Before("execution(* foo())")
        public void before() {
            System.out.println("before");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("after");
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint pj) {
            try {
                System.out.println("around before...");
                Object proceed = pj.proceed();
                System.out.println("around after...");
                return proceed;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            } finally {

            }
        }
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }
}
